package com.ssd.handler.wechat;

import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.net.url.UrlPath;
import cn.hutool.core.net.url.UrlQuery;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

import com.ssd.exception.LogicException;
import com.ssd.constant.TradingConstant;
import com.ssd.enums.TradingEnum;
import com.ssd.handler.wechat.response.WeChatResponse;
import com.ssd.model.domain.PayChannel;
import com.ssd.service.PayChannelService;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;

import java.io.ByteArrayInputStream;
import java.net.URI;
import java.security.PrivateKey;
import java.util.Map;

/**
 * 微信支付远程调用对象
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WechatPayHttpClient {

    private String mchId; //商户号
    private String appId; //商户号
    private String privateKey; //私钥字符串
    private String mchSerialNo; //商户证书序列号
    private String apiV3Key; //V3密钥
    private String domain; //请求域名
    private String notifyUrl; //请求地址

    /**
     * 获取微信支付的HTTP客户端：返回当前类的对象实例
     * 本方法通过企业ID获取支付渠道配置，并构建微信支付的客户端对象
     * @param enterpriseId 企业ID，用于获取特定企业的支付配置
     * @return 返回一个配置好的WechatPayHttpClient对象，用于进行微信支付操作
     * @throws LogicException 如果未找到对应的支付渠道配置，抛出异常
     */
    public static WechatPayHttpClient get(Long enterpriseId) {
        // 查询配置
        PayChannelService payChannelService = SpringUtil.getBean(PayChannelService.class);
        PayChannel payChannel = payChannelService.findByEnterpriseId(enterpriseId, TradingConstant.TRADING_CHANNEL_WECHAT_PAY);

        // 如果配置为空，则抛出异常
        if (ObjectUtil.isEmpty(payChannel)) {
            throw new LogicException( TradingEnum.CONFIG_EMPTY.getValue());
        }

        // 从支付渠道的其他配置中解析出微信支付所需的配置信息
        JSONObject otherConfig = JSONUtil.parseObj(payChannel.getOtherConfig());

        // 使用解析出的配置信息构建微信支付的客户端对象
        return WechatPayHttpClient.builder()
                .appId(payChannel.getAppId())
                .domain(payChannel.getDomain())
                .privateKey(payChannel.getMerchantPrivateKey())
                .mchId(otherConfig.getStr("mchId"))
                .mchSerialNo(otherConfig.getStr("mchSerialNo"))
                .apiV3Key(otherConfig.getStr("apiV3Key"))
                .notifyUrl(payChannel.getNotifyUrl())
                .build();
    }

    /**
     * 构建CloseableHttpClient远程请求对象
     * @return {@link CloseableHttpClient}
     */
    public CloseableHttpClient createHttpClient() throws Exception {
        // 加载商户私钥（privateKey：私钥字符串）
        PrivateKey merchantPrivateKey = PemUtil
                .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));

        // 加载平台证书（mchId：商户号,mchSerialNo：商户证书序列号,apiV3Key：V3密钥）
        PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, merchantPrivateKey);
        WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(
                mchId, privateKeySigner);

        // 向证书管理器增加需要自动更新平台证书的商户信息
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        certificatesManager.putMerchant(mchId, wechatPay2Credentials, apiV3Key.getBytes("utf-8"));

        // 初始化httpClient
        return com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, mchSerialNo, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(certificatesManager.getVerifier(mchId)))
                .build();
    }

    /**
     * 支持post请求的远程调用
     * @param apiPath api地址
     * @param params 携带请求参数
     * @return 返回字符串
     */
    public WeChatResponse doPost(String apiPath, Map<String, Object> params) throws Exception {
        // 格式化URL
        String url = StrUtil.format("https://{}{}", this.domain, apiPath);
        HttpPost httpPost = new HttpPost(url);

        // 设置请求头，表示接受和发送的数据类型为JSON
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        // 将请求参数转换为JSON格式的字符串
        String body = JSONUtil.toJsonStr(params);
        // 将JSON字符串设置为请求的实体内容
        httpPost.setEntity(new StringEntity(body, CharsetUtil.UTF_8));

        // 执行HTTP请求并获取响应
        CloseableHttpResponse response = this.createHttpClient().execute(httpPost);

        // 返回微信响应对象
        return new WeChatResponse(response);
    }

    /**
     * 支持get请求的远程调用
     * @param apiPath api地址
     * @param params 在路径中请求的参数
     * @return 返回字符串
     */
    public WeChatResponse doGet(String apiPath, Map<String, Object> params) throws Exception {
        // 构建请求的URI
        URI uri = UrlBuilder.create()
                .setHost(this.domain)
                .setScheme("https")
                .setPath(UrlPath.of(apiPath, CharsetUtil.CHARSET_UTF_8))
                .setQuery(UrlQuery.of(params))
                .setCharset(CharsetUtil.CHARSET_UTF_8)
                .toURI();
        // 使用构建的URI执行GET请求并返回响应
        return this.doGet(uri);
    }

    /**
     * 支持get请求的远程调用
     * @param apiPath api地址
     * @return 返回字符串
     */
    public WeChatResponse doGet(String apiPath) throws Exception {
        // 构建请求的URI
        URI uri = UrlBuilder.create()
                .setHost(this.domain)
                .setScheme("https")
                .setPath(UrlPath.of(apiPath, CharsetUtil.CHARSET_UTF_8))
                .setCharset(CharsetUtil.CHARSET_UTF_8)
                .toURI();
        // 调用doGet方法进行远程调用
        return this.doGet(uri);
    }

    /**
     * 发送GET请求并获取响应
     * 本方法用于执行到微信接口的HTTP GET请求，并返回解析后的响应对象
     * @param uri 请求的完整URI，包含所有必要的查询参数
     * @return WeChatResponse对象，封装了微信接口的响应
     * @throws Exception 如果请求执行失败或解析响应时出现问题，则抛出异常
     */
    private WeChatResponse doGet(URI uri) throws Exception {
        // 创建HttpGet对象，用于执行HTTP GET请求
        HttpGet httpGet = new HttpGet(uri);

        // 设置请求头，指示服务器返回JSON格式的数据
        httpGet.addHeader("Accept", "application/json");

        // 执行HTTP GET请求，并获取响应
        CloseableHttpResponse response = this.createHttpClient().execute(httpGet);

        // 将HTTP响应封装到WeChatResponse对象中，并返回
        return new WeChatResponse(response);
    }

}
